﻿using EkStudio.com.common.unit;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;

namespace EkStudio.com.bean.xml.reflection.JAXB
{

    public class JAXBContext<T>
    {
        private log4net.ILog logger = log4net.LogManager.GetLogger(typeof(JAXBContext<T>));

        public JAXBContext()
        {
            //获取当前执行代码的程序集
            //Assembly assem = Assembly.GetExecutingAssembly();
        }

        /// <summary>
        /// 读取xml转换成类
        /// </summary>
        /// <param name="xml"></param>
        /// <returns>类对象</returns>
        public T unmarshal(string xml)
        {
            T result = default(T);
            Type type = typeof(T);
            XmlDocument XmlDoc = new XmlDocument();
            try
            {
                XmlDoc.LoadXml(xml);
            }
            catch
            {
                throw new JAXBException("xml格式错误无法转化为类对象");
            }

            try
            {
                result = (T)Activator.CreateInstance(type);//反射构造类
            }
            catch
            {
                throw new JAXBException(string.Format("类[{0}]不存在默认构造函数", result.GetType().Name));
            }
            if (result != null)
            {
                System.Xml.XmlElement XmlRoot = XmlDoc.DocumentElement;
                string ClassRootName = this.LowCaseFirstChar(type.Name);
                XmlRootElement attr = type.GetCustomAttribute<XmlRootElement>();
                if (attr == null)
                {
                    throw new JAXBException(string.Format("类[{0}]不存在[XmlRootElement]注解", type.Name));
                }
                ClassRootName = attr.Name == "" ? ClassRootName : attr.Name;
                if (XmlRoot.Name != ClassRootName)
                {
                    logger.Error(string.Format("当前类根为[{0}],xml根为[{1}]", ClassRootName, XmlRoot.Name));
                    logger.Error(xml);
                    throw new JAXBException(string.Format("类[{0}]的[XmlRootElement]注解根名字与文档根节点名称不匹配", type.Name));
                }

                if (XmlRoot.HasChildNodes)
                {
                    this.MakerObjectByXml(result, XmlRoot);
                }
            }


            return result;
        }
        /// <summary>
        /// 将xml节点转化类对象
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="XmlNode"></param>
        private void MakerObjectByXml(object parent,XmlNode XmlNode)
        {
            
            if (parent == null) return;
            Type ClazzType = parent.GetType() ;
            //logger.Info("将xml转化成类[" + ClazzType.Name + "]");
            //logger.Info("xml==" + XmlNode.OuterXml);
            PropertyInfo[] Properties = ClazzType.GetProperties();//获取类 属性
            if (Properties.Length > 0)
            {
                for (int k = 0; k < Properties.Length; k++)
                {
                    PropertyInfo Property = Properties[k];
                    if (Property.CanWrite && Property.GetCustomAttribute<XmlTransient>() == null) 
                    {
                        String PropertyName = this.LowCaseFirstChar(Property.Name);//对应Xml节点名称
                        bool ElementNode = true;//是否是节点
                        XmlAttribute NodeAttr = Property.GetCustomAttribute<XmlAttribute>();
                        if (NodeAttr != null)
                        {
                            if(NodeAttr.Name != "")
                            {
                                PropertyName = NodeAttr.Name;
                            }
                            
                            ElementNode = false;
                        }
                        else
                        {
                            XmlElement NodeElement = Property.GetCustomAttribute<XmlElement>();
                            if (NodeElement != null )
                            {
                                if(NodeElement.Name != "") { PropertyName = NodeElement.Name; }
                            }
                        }
                        Type ReturnType = Property.GetMethod.ReturnType;//返回值类型
                        //只有允许字符、数字、布尔型允许是Attribute类型节点
                        if (!ElementNode && ReturnType != typeof(string) && ReturnType == typeof(int) && ReturnType == typeof(bool))
                        {
                            ElementNode = true;
                        }
                        #region 当前属性值是 字符、数字、布尔型
                        if (ReturnType == typeof(string)|| ReturnType == typeof(int) || ReturnType == typeof(bool))
                        {
                            string value = "null";
                            //--------------开始取值-------------------------------------
                            if (ElementNode)//节点
                            {
                                XmlNode ThisNode = XmlNode.SelectSingleNode(PropertyName);
                                if(ThisNode != null)
                                {
                                    value = ThisNode.InnerText;
                                }
                            }
                            else //节点属性
                            {
                                System.Xml.XmlAttribute attr = XmlNode.Attributes[PropertyName];
                                if (attr != null)
                                {
                                    value = attr.Value;
                                } 
                            }
                            //-------------------给值--------------------------------------
                            if (value != "null")
                            {
                                if (ReturnType == typeof(string))
                                {
                                    Property.SetValue(parent, value);
                                }
                                else if (ReturnType == typeof(int))
                                {
                                    try
                                    {
                                        int val = int.Parse(value);
                                        Property.SetValue(parent, val);
                                    }
                                    catch { }
                                }
                                else if (ReturnType == typeof(bool))
                                {
                                    try
                                    {
                                        bool val = bool.Parse(value);
                                        Property.SetValue(parent, val);
                                    }
                                    catch { }
                                }
                            }
                            
                            //logger.Info(string.Format("开始取值节点[{0}]==[{1}],节点类型[{2}]]", PropertyName,value, ElementNode.ToString()));
                        }
                        #endregion
                        #region 列表list类型处理
                        else if (ReturnType.IsGenericType && ReturnType.Name == typeof(List<>).Name)
                        {
                            XmlNodeList ElementList = XmlNode.SelectNodes(PropertyName);
                            if(ElementList!=null && ElementList.Count > 0)
                            {
                                Type ListType = typeof(List<>);
                                //获取泛型类型
                                Type[] Arguments = ReturnType.GetGenericArguments();
                                Type MakerType = ListType.MakeGenericType(Arguments);
                                object list = Activator.CreateInstance(MakerType);
                                Property.SetValue(parent, list);
                                for(int i = 0; i < ElementList.Count; i++)
                                {
                                    XmlNode ThisNode = ElementList[i];
                                    Type ArgumentItemType = Arguments[0];
                                    object ArgumentItem = Activator.CreateInstance(ArgumentItemType);

                                    this.MakerObjectByXml(ArgumentItem, ThisNode);

                                    MethodInfo ListAdd = MakerType.GetMethod("Add");
                                    if (ListAdd != null)
                                    {
                                        //logger.Info(string.Format("向类[{0}]中添加List数据行[{1}]",ClazzType.Name, ArgumentItemType.Name));
                                        ListAdd.Invoke(list, new Object[] { ArgumentItem });
                                    }
                                }
                            }
                        }
                        #endregion
                        #region 当前返回值对象为类
                        else //类
                        {
                            XmlNode ThisNode = XmlNode.SelectSingleNode(PropertyName);
                            if (ThisNode != null)
                            {
                                object SubTarget = Activator.CreateInstance(ReturnType);
                                Property.SetValue(parent, SubTarget);
                                this.MakerObjectByXml(SubTarget, ThisNode);
                            }
                        }
                        #endregion
                    }
                }
            } 
        }





        /// <summary>
        /// 类转化成xml字符
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string marshal(T param,bool format)
        {
            XmlDocument xml = new XmlDocument();
            Type BeanType = param.GetType();

            Attribute att = BeanType.GetCustomAttribute(typeof(XmlRootElement));
            string RootPath = "";
            if (att != null)
            {
                XmlRootElement root = (XmlRootElement)att;
                if (root.Name == "")
                {
                    RootPath = BeanType.Name;
                }
                else
                {
                    RootPath = root.Name;
                }
            }
            else
            {
                throw new JAXBException(string.Format("类[{0}]中未能找到注解[XmlRootElement]", param.GetType().Name));
            }


            xml.AppendChild(xml.CreateXmlDeclaration("1.0", "utf-8", ""));
            System.Xml.XmlElement XmlRoot = xml.CreateElement(RootPath);
            xml.AppendChild(XmlRoot);

            //获取排序
            string[] ListOrder = null;
            att = BeanType.GetCustomAttribute(typeof(XmlType));
            if (att != null)
            {
                XmlType orderBy = (XmlType)att;
                ListOrder = orderBy.FieldOrder;

            }

            #region 存在排序
            if (ListOrder != null)
            {
                
            }
            #endregion
            else
            #region 不存在排序
            {

                //获取属性成员
                PropertyInfo[] Properties = BeanType.GetProperties();
                if (Properties.Length > 0)
                {
                    for (int i = 0; i < Properties.Length; i++)
                    {
                        PropertyInfo it = Properties[i];
                        if (it.GetCustomAttribute(typeof(XmlTransient)) != null) continue;
                        string path = it.Name;
                        bool EleNode = true;
                        //获取名称
                        Object value = this.getPropertyInfo(param, it, ref path, ref EleNode);
                        if (value != null)
                            this.ObjectToXml(value, path, EleNode, XmlRoot, xml);
                    }
                }
            }

            #endregion
            StringBuilder sb = new StringBuilder();
            if (format)
            {
                StringWriter sw = new StringWriter(sb);
                XmlTextWriter xtw = null;
                try
                {
                    xtw = new XmlTextWriter(sw);
                    xtw.Formatting = Formatting.Indented;
                    //xtw.Indentation = 1;
                    //xtw.IndentChar = '\t';
                    xml.WriteTo(xtw);
                }
                finally
                {
                    if (xtw != null)
                        xtw.Close();
                }
            }
            else
            {
                sb.Append(xml.OuterXml);
            }
            
            

            return sb.ToString();
        }
        /// <summary>
        /// 类转换成xml文档
        /// </summary>
        /// <param name="obj">类对象</param>
        /// <param name="NodeName">节点名称</param>
        /// <param name="Element">是否是Element节点,否则Attribute节点</param>
        /// <param name="PXmlNode">父节点根</param>
        /// <param name="doc">文档管理器</param>
        private void ObjectToXml(object obj, string NodeName, bool Element, System.Xml.XmlElement PXmlNode, XmlDocument doc)
        {
            try
            {
                if (obj is string )
                {
                    string value = (string)obj;
                    if (Element)
                    {
                        System.Xml.XmlElement NodeElement = doc.CreateElement(NodeName);
                        if (value.Trim() != "")
                        {
                            NodeElement.InnerText = value.Trim() ;
                        }
                        PXmlNode.AppendChild(NodeElement);
                    }
                    else
                    {
                        System.Xml.XmlAttribute NodeAttr = doc.CreateAttribute(NodeName);
                        NodeAttr.Value = obj.ToString();
                        PXmlNode.Attributes.Append(NodeAttr);
                    }

                }
                else if(obj is int)
                {
                    int value = (int)obj;
                    if (Element)
                    {
                        System.Xml.XmlElement NodeElement = doc.CreateElement(NodeName);
                        NodeElement.InnerText = Convert.ToString(value);
                        PXmlNode.AppendChild(NodeElement);
                    }
                    else
                    {
                        System.Xml.XmlAttribute NodeAttr = doc.CreateAttribute(NodeName);
                        NodeAttr.Value = Convert.ToString(value);
                        PXmlNode.Attributes.Append(NodeAttr);
                    }
                }
                else if(obj is bool)
                {
                    bool BoolStr = (bool)obj;
                    if (Element)
                    {

                        System.Xml.XmlElement NodeElement = doc.CreateElement(NodeName);
                        NodeElement.InnerText = BoolStr?"true":"false";
                        PXmlNode.AppendChild(NodeElement);
                    }
                    else
                    {
                        System.Xml.XmlAttribute NodeAttr = doc.CreateAttribute(NodeName);
                        NodeAttr.Value = BoolStr ? "true" : "false";
                        PXmlNode.Attributes.Append(NodeAttr);
                    }
                }
                else if (obj.GetType().IsGenericType && obj is IEnumerable)
                { //当前值是list对象
                    try
                    {
                        IEnumerable list = obj as IEnumerable;
                        foreach (var it in list)
                        {
                            this.ObjectToXml(it, NodeName, true, PXmlNode, doc);
                        }
                    }
                    catch (Exception e)
                    {
                        logger.Error("LIST转化失败" + e.Message);
                    }

                }
                else
                {
                    System.Xml.XmlElement NodeElement = doc.CreateElement(NodeName);
                    PXmlNode.AppendChild(NodeElement);
                    //获取类对象属性
                    Type ObjType = obj.GetType();

                    //获取属性成员
                    PropertyInfo[] Properties = ObjType.GetProperties();
                    if (Properties.Length > 0)
                    {
                        for (int i = 0; i < Properties.Length; i++)
                        {
                            PropertyInfo it = Properties[i];
                            if (it.GetCustomAttribute(typeof(XmlTransient)) != null) continue;
                            string path = it.Name;
                            bool EleNode = true;
                            Object value = this.getPropertyInfo(obj, it, ref path, ref EleNode);
                            if (value != null)
                                this.ObjectToXml(value, path, EleNode, NodeElement, doc);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                logger.Error(e.Message);
            }


        }
        /// <summary>
        /// 获取属性值同时输出节点名称并输出当前属性是xml的Element还是Attribute
        /// </summary>
        /// <param name="PValue">待反射取值的Object</param>
        /// <param name="property">类属性</param>
        /// <param name="NodeName">输出的节点名称</param>
        /// <param name="EleNode">输出的节点属性</param>
        /// <returns>通过反射取到的值</returns>
        private object getPropertyInfo(object PValue, PropertyInfo property, ref string NodeName, ref bool EleNode)
        {
            object value = null;
            if (property.CanRead)
            {
                NodeName = this.LowCaseFirstChar(NodeName);
                EleNode = true;
                Attribute attr = property.GetCustomAttribute(typeof(XmlAttribute));
                if (attr != null)
                {
                    XmlAttribute attribute = (XmlAttribute)attr;
                    if (attribute.Name != "") NodeName = attribute.Name;
                    EleNode = false;
                }
                attr = property.GetCustomAttribute(typeof(XmlElement));
                if (attr != null)
                {
                    XmlElement attribute = (XmlElement)attr;
                    if (attribute.Name != "") NodeName = attribute.Name;
                }
                try
                {
                    value = property.GetValue(PValue, null);
                }
                catch (Exception e)
                {
                    value = null;
                    logger.Error("GetValue(" + property.Name + ")  " + "className=" + PValue.GetType().Name + " " + e.Message);
                }
            }



            return value;
        }
        /// <summary>
        /// 小写首字母
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private string LowCaseFirstChar(string arg)
        {
            string result = "";
            if (arg.Length > 0)
            {
                result = arg.Substring(0, 1).ToLower() + arg.Substring(1);
            }

            return result;
        }

    }
}
